package com.lintf.starter.central.auth;

import com.lintf.starter.central.auth.cache.CacheStrategy;
import com.lintf.starter.central.auth.exception.AuthException;
import lombok.extern.slf4j.Slf4j;

import java.time.Instant;
import java.util.Date;
import java.util.UUID;

/**
 * CentralAuthClient类负责与中央认证服务进行交互，提供认证和令牌管理功能。
 * 它根据配置信息和缓存策略来获取、刷新和验证访问令牌。
 */
@Slf4j
public class CentralAuthClient {

    // CentralAuthProperties对象用于存储客户端的认证配置信息
    private final CentralAuthProperties properties;
    // CacheStrategy对象用于实现令牌的缓存逻辑
    private final CacheStrategy cacheStrategy;

    /**
     * 构造函数，初始化CentralAuthClient实例。
     *
     * @param properties    认证配置信息
     * @param cacheStrategy 缓存策略实现
     */
    public CentralAuthClient(CentralAuthProperties properties,
                             CacheStrategy cacheStrategy) {
        this.properties = properties;
        this.cacheStrategy = cacheStrategy;
    }

    /**
     * 获取当前有效的访问令牌。如果缓存中存在且未过期，则直接返回缓存的令牌；
     * 否则，将尝试刷新令牌并返回新的令牌。
     *
     * @return 当前有效的访问令牌
     */
    public String getAccessToken() {
        CentralAuthProperties.CacheType cacheType = properties.getCacheType();
        log.info("获取访问令牌，当前缓存策略: {}", cacheType);
        if (cacheType == CentralAuthProperties.CacheType.NONE) {
            log.info("使用无操作缓存策略，不进行令牌缓存");
            CustomToken newToken = refreshToken();
            log.info("刷新令牌成功: {}", newToken);
            return newToken.getValue();
        }

        // 尝试从缓存中获取令牌
        CustomToken cachedToken = cacheStrategy.getToken(properties.getClientId());

        // 如果缓存的令牌存在且有效，则直接返回其值
        if (cachedToken != null && isValid(cachedToken)) {
            log.info("从缓存中获取令牌: {}", cachedToken);
            return cachedToken.getValue();
        }

        log.info("缓存中没有有效令牌，尝试刷新令牌...");
        // 刷新令牌，并将其存入缓存
        CustomToken newToken = refreshToken();
        cacheStrategy.cacheToken(properties.getClientId(), newToken);
        log.info("刷新令牌成功: {}", newToken);
        return newToken.getValue();
    }

    /**
     * 验证给定的令牌是否有效。
     *
     * @param token 待验证的令牌
     * @return 如果令牌有效，则返回true；否则返回false
     */
    public boolean isValid(CustomToken token) {
        // 如果令牌或其过期时间为空，则令牌无效
        if (token == null || token.getExpiry() == null) return false;
        // 提前阈值刷新（比如在过期前5分钟）
        Instant expiry = token.getExpiry().toInstant().minus(properties.getRefreshWindow());
        // 如果当前时间在令牌过期时间之前，则令牌有效
        return Instant.now().isBefore(expiry);
    }

    /**
     * 刷新并获取新的访问令牌。
     * 此方法应实现与认证服务器通信以获取新令牌的逻辑。
     *
     * @return 新的访问令牌
     */
    public CustomToken refreshToken() {
        // 实现获取新令牌的逻辑
        // 这里简化为模拟实现
        return new CustomToken(UUID.randomUUID().toString(),
                Date.from(Instant.now().plus(properties.getTokenTtl())));
    }

    /**
     * 获取令牌的元数据，即令牌的详细信息。
     *
     * @return 令牌的元数据
     */
    public CustomToken getTokenMetadata() {
        // 从缓存中获取令牌信息
        return cacheStrategy.getToken(properties.getClientId());
    }

    /**
     * 检查认证客户端的健康状态。
     * 尝试获取访问令牌，如果成功，则表示客户端状态健康。
     *
     * @return 如果客户端状态健康，则返回true；否则返回false
     */
    public boolean isHealthy() {
        try {
            // 尝试获取访问令牌来验证客户端是否能正常工作
            getAccessToken();
            return true;
        } catch (AuthException e) {
            // 如果获取令牌失败，则认为客户端状态不健康
            return false;
        }
    }
}
